Skip to content

fix(patches): return empty object for unhandled manifests in loadManifest#1151

Merged
conico974 merged 4 commits intoopennextjs:mainfrom
nathanschram:fix/load-manifest-graceful-fallback
Mar 21, 2026
Merged

fix(patches): return empty object for unhandled manifests in loadManifest#1151
conico974 merged 4 commits intoopennextjs:mainfrom
nathanschram:fix/load-manifest-graceful-fallback

Conversation

@nathanschram
Copy link
Contributor

Summary

  • Return {} instead of throwing for manifest paths not found during the build-time glob scan in loadManifest() and evalManifest()
  • Handles Next.js 16.2.0-canary.53+ which introduces new loadManifest() calls for optional/phase-dependent manifests

Problem

Next.js canary adds loadManifest() calls for:

  1. subresource-integrity-manifest.json - only generated when experimental.sri is configured, but loaded unconditionally
  2. Per-route react-loadable-manifest.json - generated by Turbopack, possibly in a different build phase

The adapter's build-time glob scan doesn't find these files, so the patched function throws at runtime, crashing all dynamic routes with 500.

Fix

Replace the throw new Error('Unexpected loadManifest...') fallback with return {}. This follows the same defensive pattern used by other adapter plugins (instrumentation.ts, find-dir.ts) that return safe defaults for missing files.

Test plan

  • Build a Next.js app with next@canary (16.2.0-canary.53+) using @opennextjs/cloudflare
  • Verify dynamic/SSR routes no longer return 500
  • Verify existing manifests still load correctly (app-manifest, build-manifest, etc.)
  • Verify SRI works correctly when experimental.sri IS configured

Fixes #1141

@changeset-bot
Copy link

changeset-bot bot commented Feb 25, 2026

🦋 Changeset detected

Latest commit: a695016

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@opennextjs/cloudflare Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@nathanschram
Copy link
Contributor Author

Hey @vicb - just wanted to check in on this. No rush at all, I know you're probably busy. Just wanted to make sure the approach looks reasonable to you, or if you'd prefer I take it in a different direction. Happy to adjust anything.

@Rovak
Copy link

Rovak commented Mar 19, 2026

Can confirm that is working for me in production after copying this patch into the project using pnpm patch

@bonadio
Copy link

bonadio commented Mar 20, 2026

Need this fix too.
Thanks

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 20, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@opennextjs/cloudflare@1151

commit: a695016

@sommeeeer
Copy link
Collaborator

sommeeeer commented Mar 20, 2026

LGTM, until this is merged you can use npm i https://pkg.pr.new/@opennextjs/cloudflare@1151.

// This handles optional manifests (e.g. subresource-integrity-manifest.json when
// experimental.sri is not configured) and manifests generated in a different build
// phase (e.g. per-route react-loadable-manifest.json with Turbopack).
return {};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is the right solution, this will just hide other potential errors, if we know the one that could be null, that's the one we should return an empty object, not everything

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call - updated to handle only the specific manifests that Next.js loads with handleMissing: true (traced through route-module.ts in vercel/next.js).

The five optional manifests for loadManifest:

  • react-loadable-manifest.json - Turbopack per-route, not all routes have dynamic imports
  • subresource-integrity-manifest.json - only when experimental.sri configured
  • server-reference-manifest.json - App Router only, Pages Router never generates
  • dynamic-css-manifest.json - Pages Router + Webpack only
  • fallback-build-manifest.json - only for /_error page

Everything else still throws. Same approach for evalManifest - only _client-reference-manifest.js gets a safe default ({ __RSC_MANIFEST: {} }) since it's optional for static metadata routes.

$PATH = $PATH.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)});
${returnManifests}
throw new Error(\`Unexpected evalManifest(\${$PATH}) call!\`);
return {};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed - evalManifest now only handles _client-reference-manifest.js (optional for static metadata routes per route-module.ts line 343). Returns { __RSC_MANIFEST: {} } as the safe default structure. Everything else still throws.

Address review feedback from @conico974 — instead of returning {} for all
unknown manifests (which would hide genuine errors), only handle the specific
manifests that Next.js loads with handleMissing: true.

loadManifest optional manifests (from vercel/next.js route-module.ts):
- react-loadable-manifest.json (Turbopack per-route)
- subresource-integrity-manifest.json (experimental.sri)
- server-reference-manifest.json (App Router only)
- dynamic-css-manifest.json (Pages Router + Webpack only)
- fallback-build-manifest.json (/_error page only)

evalManifest optional manifests:
- _client-reference-manifest.js (static metadata routes)

Everything else still throws to surface genuine errors.
@bonadio
Copy link

bonadio commented Mar 20, 2026

LGTM, until this is merged you can use npm i https://pkg.pr.new/@opennextjs/cloudflare@1151.

This did not work for me, trying to run next 16.2 still receives "Unexpected loadManifest(/.next/server/prefetch-hints.json) call!" running with wrangler dev locally

@matthewvolk
Copy link

matthewvolk commented Mar 20, 2026

Hey @nathanschram — we've been testing Next.js 16.2.0 (stable, not canary) on Cloudflare Workers and can confirm this PR is needed alongside #1160 for things to work.

We found one issue during testing (after manually applying the patch from this PR locally): some manifests are requested without the .json extension, so the $PATH.endsWith("...-manifest.json") checks don't match them.

Root cause

Next.js defines some manifest constants without .json:

  • SUBRESOURCE_INTEGRITY_MANIFEST = "subresource-integrity-manifest" (source)
  • DYNAMIC_CSS_MANIFEST = "dynamic-css-manifest" (same file)

When loadManifest() is called with these constants, the path ends up as e.g. /.next/server/dynamic-css-manifest — no .json extension.

My suggestion for this PR

For each manifest in the fallback list, check both with and without .json:

$PATH.endsWith("dynamic-css-manifest.json") || $PATH.endsWith("dynamic-css-manifest")

Or strip .json from the path before matching:

const normalizedPath = $PATH.replace(/\.json$/, "");
if (normalizedPath.endsWith("dynamic-css-manifest")) return {};

After manually applying (locally) #1160 + this PR + the extensionless fix above, I can confirm that our Next.js 16.2.0 app renders successfully on Cloudflare Workers (for Platforms, in our case):

next-16-2-0-running-after-manual-patches

Happy to help with the fix or open a follow-up PR if that's easier. Thanks for this PR!

Copy link
Collaborator

@conico974 conico974 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Thanks for the fix

@conico974
Copy link
Collaborator

@nathanschram Could You fix the prettier issue please ?

- Fix prettier formatting in changeset
- Strip .json extension before matching optional manifests since Next.js
  defines some constants without it (SUBRESOURCE_INTEGRITY_MANIFEST,
  DYNAMIC_CSS_MANIFEST, SERVER_REFERENCE_MANIFEST)
- Add prefetch-hints to the optional manifest list (new in Next.js 16.2)
- All 236 tests pass, tsc clean, prettier clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@nathanschram
Copy link
Contributor Author

@conico974 Prettier issue should be fixed now - sorry about that.

@matthewvolk Great catch on the extensionless manifests - I've updated the matching to strip .json before comparison, so it now handles both dynamic-css-manifest and dynamic-css-manifest.json. Verified against the Next.js constants in shared/lib/constants.ts where SUBRESOURCE_INTEGRITY_MANIFEST, DYNAMIC_CSS_MANIFEST, and SERVER_REFERENCE_MANIFEST are all defined without the extension.

@bonadio Added prefetch-hints to the optional manifest list as well - it's a new constant (PREFETCH_HINTS = 'prefetch-hints.json') in Next.js 16.2.

Summary of changes in the latest push:

  • Prettier fix on changeset
  • Extensionless manifest matching (strips .json before checking)
  • prefetch-hints added to the optional list
  • All 236 tests pass, tsc clean

@conico974 conico974 merged commit a143282 into opennextjs:main Mar 21, 2026
7 checks passed
conico974 added a commit to opennextjs/adapters-api that referenced this pull request Mar 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Unexpected loadManifest calls for new manifest files introduced in Next.js 16.2.0-canary

6 participants